/**
 * This file is part of the Open Web Application Security Project (OWASP) Java File IO Security project. For details, please see
 * <a href="https://www.owasp.org/index.php/OWASP_Java_File_I_O_Security_Project">https://www.owasp.org/index.php/OWASP_Java_File_I_O_Security_Project</a>.
 *
 * Copyright (c) 2014 - The OWASP Foundation
 *
 * This API is published by OWASP under the Apache 2.0 license. You should read and accept the LICENSE before you use, modify, and/or redistribute this software.
 *
 * @author August Detlefsen <a href="http://www.codemagi.com">CodeMagi</a> - Java File IO Security Project lead
 * @created 2014
 */
package org.owasp.fileio.codecs;

import java.io.IOException;
import java.io.PushbackReader;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;

public interface Trie<T> extends Map<CharSequence, T> {

    public Map.Entry<CharSequence, T> getLongestMatch(CharSequence key);

    public Map.Entry<CharSequence, T> getLongestMatch(PushbackReader keyIn) throws IOException;

    public int getMaxKeyLength();

    static class TrieProxy<T> implements Trie<T> {

	private Trie<T> wrapped;

	TrieProxy(Trie<T> toWrap) {
	    wrapped = toWrap;
	}

	protected Trie<T> getWrapped() {
	    return wrapped;
	}

	public Map.Entry<CharSequence, T> getLongestMatch(CharSequence key) {
	    return wrapped.getLongestMatch(key);
	}

	public Map.Entry<CharSequence, T> getLongestMatch(PushbackReader keyIn) throws IOException {
	    return wrapped.getLongestMatch(keyIn);
	}

	public int getMaxKeyLength() {
	    return wrapped.getMaxKeyLength();
	}

	/* java.util.Map: */
	public int size() {
	    return wrapped.size();
	}

	public boolean isEmpty() {
	    return wrapped.isEmpty();
	}

	public boolean containsKey(Object key) {
	    return wrapped.containsKey(key);
	}

	public boolean containsValue(Object val) {
	    return wrapped.containsValue(val);
	}

	public T get(Object key) {
	    return wrapped.get(key);
	}

	public T put(CharSequence key, T value) {
	    return wrapped.put(key, value);
	}

	public T remove(Object key) {
	    return wrapped.remove(key);
	}

	public void putAll(Map<? extends CharSequence, ? extends T> t) {
	    wrapped.putAll(t);
	}

	public void clear() {
	    wrapped.clear();
	}

	public Set<CharSequence> keySet() {
	    return wrapped.keySet();
	}

	public Collection<T> values() {
	    return wrapped.values();
	}

	public Set<Map.Entry<CharSequence, T>> entrySet() {
	    return wrapped.entrySet();
	}

	public boolean equals(Object other) {
	    return wrapped.equals(other);
	}

	public int hashCode() {
	    return wrapped.hashCode();
	}
    }

    static class Unmodifiable<T> extends TrieProxy<T> {

	Unmodifiable(Trie<T> toWrap) {
	    super(toWrap);
	}

	public T put(CharSequence key, T value) {
	    throw new UnsupportedOperationException("Unmodifiable Trie");
	}

	public T remove(CharSequence key) {
	    throw new UnsupportedOperationException("Unmodifiable Trie");
	}

	public void putAll(Map<? extends CharSequence, ? extends T> t) {
	    throw new UnsupportedOperationException("Unmodifiable Trie");
	}

	public void clear() {
	    throw new UnsupportedOperationException("Unmodifiable Trie");
	}

	public Set<CharSequence> keySet() {
	    return Collections.unmodifiableSet(super.keySet());
	}

	public Collection<T> values() {
	    return Collections.unmodifiableCollection(super.values());
	}

	public Set<Map.Entry<CharSequence, T>> entrySet() {
	    return Collections.unmodifiableSet(super.entrySet());
	}
    }

    public static class Util {

	private Util() {
	}

	static <T> Trie<T> unmodifiable(Trie<T> toWrap) {
	    return new Unmodifiable<T>(toWrap);
	}
    }
}
